package com.wuyan.web.form.helper.easyexcel;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;

import java.util.Comparator;
import java.util.List;

/**
 * 导出表的内容样式设定
 */

@Slf4j
public class ContentStyleWriteHandler implements CellWriteHandler {
    /**
     * 头部数据
     */
    private final List<String> head;

    /**
     * 数据
     */
    private List<List<String>> records;

    /**
     * 字体大小
     */
    private short fontSize = 11;

    /**
     * 字体颜色
     */
    private short fontColor = HSSFColor.HSSFColorPredefined.BLACK.getIndex();

    /**
     * 头部最大列宽
     */
    private final int colHeadMaxWidth = 24;

    /**
     * 最大列宽，width为字符个数(再head的基础上扩充一倍)
     */
    private final int colMaxWidth = colHeadMaxWidth * 2;

    /**
     * 每行最大行数: 字符总宽度/colMaxWidth * 256
     */
    private final int rowMaxHeightNum = 5;

    public ContentStyleWriteHandler(List<String> head, List<List<String>> records) {
        this.head = head;
        this.records = records;
    }

    /**
     * 创建单元格之前
     *
     * @param writeSheetHolder SheetHolder
     * @param writeTableHolder TableHolder
     * @param row              行
     * @param head             头
     * @param columnIndex      列索引
     * @param relativeRowIndex 相对行索引
     * @param isHead           是否头部
     */
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder,
                                 WriteTableHolder writeTableHolder,
                                 Row row,
                                 Head head,
                                 Integer columnIndex,
                                 Integer relativeRowIndex,
                                 Boolean isHead) {
    }

    /**
     * 创建单元格之后
     *
     * @param writeSheetHolder SheetHolder
     * @param writeTableHolder TableHolder
     * @param cell             单元格
     * @param head             头部
     * @param relativeRowIndex 相对行号
     * @param isHead           是否头部
     */
    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder,
                                WriteTableHolder writeTableHolder,
                                Cell cell,
                                Head head,
                                Integer relativeRowIndex,
                                Boolean isHead) {

    }

    /**
     * 数据转换之后
     *
     * @param writeSheetHolder SheetHolder
     * @param writeTableHolder TableHolder
     * @param cellData         数据
     * @param cell             单元格
     * @param head             头部
     * @param relativeRowIndex 相对行号
     * @param isHead           是否头部
     */
    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder,
                                       WriteTableHolder writeTableHolder,
                                       CellData cellData,
                                       Cell cell,
                                       Head head,
                                       Integer relativeRowIndex,
                                       Boolean isHead) {
    }

    /**
     * 最后执行
     *
     * @param writeSheetHolder SheetHolder
     * @param writeTableHolder TableHolder
     * @param cellDataList     数据
     * @param cell             单元格
     * @param head             头部
     * @param relativeRowIndex 相对行号
     * @param isHead           是否头部
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder,
                                 WriteTableHolder writeTableHolder,
                                 List<CellData> cellDataList,
                                 Cell cell,
                                 Head head,
                                 Integer relativeRowIndex,
                                 Boolean isHead) {

        // 忽略第一行
        if (0 != cell.getRowIndex()) {
            Sheet sheet = writeSheetHolder.getSheet();

            // 设置列宽
            sheet.setColumnWidth(cell.getColumnIndex(), getColumnWidth(cell.getColumnIndex()));

            // 设置行高
            sheet.getRow(cell.getRowIndex())
                    .setHeightInPoints(getHeightInPoints(cell.getRowIndex(), sheet));

            // 单元格设置样式
            cell.setCellStyle(createCellStyle(sheet.getWorkbook(), cell));
        }
    }

    /**
     * 计算列宽: 在不影响头部宽度的情况下,重新设定列宽.根据colHeadMaxWidth和colMaxWidth
     *
     * @param colIndex 列索引
     * @return int
     */
    private int getColumnWidth(int colIndex) {
        final String[] maxContents = {null};

        // 找到数据最多的单元格数据
        for (List<String> item : records) {
            if (item.size() <= colIndex) {
                continue;
            }

            // 与头部一致时忽略
            if (head.get(colIndex).equals(item.get(colIndex))) {
                continue;
            }

            if (null == maxContents[0]) {
                maxContents[0] = item.get(colIndex);
            } else if (maxContents[0].length() < item.get(colIndex).length()) {
                maxContents[0] = item.get(colIndex);
            }
        }

        String maxContent = maxContents[0];
        if (null == maxContent) {
            return this.colHeadMaxWidth * 256;
        }

        // 未超出头部,则忽略
        int contentLength = maxContent.getBytes().length;
        if (contentLength < this.colHeadMaxWidth) {
            return this.colHeadMaxWidth * 256;
        }

        // 超出但是未超出最大列宽
        if (contentLength < this.colMaxWidth) {
            return contentLength * 256;
        }

        // 超出最大列宽
        return this.colMaxWidth * 256;
    }

    /**
     * 计算行高: 利用最大列宽 colMaxWidth 进行整除,再根据字体大小 fontSize 计算出最大行高
     *
     * @param rowIndex 行
     * @param sheet    工作表
     * @return int
     */
    private float getHeightInPoints(int rowIndex, Sheet sheet) {
        List<String> data = records.get(rowIndex);
        // 找到行中数据长度最大的数据
        String maxContent = data.stream()
                .max(Comparator.comparingInt(String::length))
                .orElse(null);

        // 找到最大列宽
        int maxWidth = -1;
        for (int i = 0; i < this.head.size(); i++) {
            if (maxWidth < sheet.getColumnWidth(i)) {
                maxWidth = sheet.getColumnWidth(i);
            }
        }
        if (maxWidth == -1) {
            maxWidth = colMaxWidth;
        }else {
            maxWidth /= 256;
        }

        // 默认
        if (null == maxContent) {
            return -1;
        }

        // 字符总宽度
        int width = maxContent.getBytes().length;

        // 只要存在余数,均向上取整
        int rows = width / maxWidth;
        rows = width % maxWidth > 0 ? rows + 1 : rows;

        return (float) ((Math.min(rows, this.rowMaxHeightNum)) * (this.fontSize * 1.5));
    }

    /**
     * 设置样式
     *
     * @param workbook workbook
     * @param cell
     */
    private CellStyle createCellStyle(Workbook workbook, Cell cell) {
        CellStyle cellStyle = workbook.createCellStyle();

        // 设置水平居中
        if (cell.getCellTypeEnum().equals(CellType.NUMERIC)
                || cell.getCellTypeEnum().equals(CellType.BOOLEAN)) {
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
        } else {
            cellStyle.setAlignment(HorizontalAlignment.LEFT);
        }

        // 设置垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 设置下边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        // 设置上边框
        cellStyle.setBorderTop(BorderStyle.THIN);
        // 设置走边框
        cellStyle.setBorderLeft(BorderStyle.THIN);
        // 设置右边框
        cellStyle.setBorderRight(BorderStyle.THIN);
        // 自动换行
        cellStyle.setWrapText(true);

        // 设置字体
        Font font = workbook.createFont();
        // 设置字号
        font.setFontHeightInPoints(fontSize);
        // 设置是否为斜体
        font.setItalic(false);
        // 设置是否加粗
        font.setBold(false);
        // 设置字体颜色
        font.setColor(fontColor);
        cellStyle.setFont(font);

        //设置背景
        cellStyle.setFillPattern(FillPatternType.NO_FILL);
        cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);

        return cellStyle;
    }
}
